package org.codetranslators.compiler.tiger.semantics;

import org.codetranslators.common.Label;
import org.codetranslators.compiler.abstractassembly.AssemType;
import org.codetranslators.compiler.mips.translate.ActivationFrame;
import org.codetranslators.compiler.tiger.abssyntree.Symbol;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

public class Level {
	private Level parentLevel;
	private ActivationFrame activationFrame;
	private Symbol name;
	
	private AssemType functionBody;     // The translated body of the function

	private Vector invocations;    // A list of function levels that represents the functions that this
								   // function calls.
	private int numIncomingParams; // The number of parameters this function accepts.
	
	private HashMap labelVsStr;    // A map of the string literals in the program versus the labels in 
								   // in the assembly language
	
	public static final String tab = "\t";
	public static final String newLine = "\r\n";
	
	public Level(Level parent, Symbol levelName)
	{
		this.parentLevel = parent;
		this.name  = levelName;
		
		// Create a new activation frame
		Label newLabel = new Label();
		activationFrame = new ActivationFrame(newLabel);
		
		invocations = new Vector();
		
		labelVsStr = new HashMap();	
	}
	
	public Level getParentLevel()
	{
		return parentLevel;
	}
	
	public ActivationFrame getActivationFrame()
	{
		return activationFrame;
	}
	
	public Symbol getLevelName()
	{
		return name;
	}

	public AssemType getFunctionBody() {
		return functionBody;
	}

	public void setFunctionBody(AssemType functionBody) {
		this.functionBody = functionBody;
	}

	public void addCalledFunction(Level calledFunction)
	{
		invocations.addElement(calledFunction);
	}

	public int getNumIncomingParams() {
		return numIncomingParams;
	}

	public void setNumIncomingParams(int numIncomingParams) {
		this.numIncomingParams = numIncomingParams;
	}
	
	public int getMaxOutgoingParams()
	{
		int maxOutgoingParams = 0;
		int numInvocations = invocations.size();
		for(int i = 0; i < numInvocations; i++)
		{
			Level calledFunction = (Level) invocations.elementAt(i);
			int incomingParamsForCalledFunction = calledFunction.getNumIncomingParams();
			if(incomingParamsForCalledFunction > maxOutgoingParams)
				maxOutgoingParams = incomingParamsForCalledFunction;
		}
		
		if(maxOutgoingParams > 4)
			maxOutgoingParams = maxOutgoingParams - 4;
		else
			maxOutgoingParams = 0;
		
		return maxOutgoingParams;
	}
	
	/**
	 * Returns the number of functions called by the function represented by this level
	 * @return
	 */
	public int getNumFunctionsCalled()
	{
		return invocations.size();
	}

	public HashMap getLabelVsString() {
		return labelVsStr;
	}
	
	public void setStringLabel(String str, Label strLabel)
	{
		labelVsStr.put(strLabel, str);
	}
	
	/**
	 * 
	 */
	public Vector dataDeclarations()
	{
		Vector decls = new Vector();
		
		Set entries = labelVsStr.entrySet();
		Iterator iter = entries.iterator();
		while(iter.hasNext())
		{
			Map.Entry entry = (Map.Entry) iter.next();
			Label lbl = (Label) entry.getKey();
			String str = (String)  entry.getValue();
			
			String declaration = lbl + ": " + ".asciiz \"" + str +  "\"" + newLine;
			decls.addElement(declaration);
		}
		
		return decls;
	}
}
